/*
 * Orchard Core 中实现了基于角色的访问控制，建议用于授权。
 * 有不同的角色，每个角色都有一组权限（例如查看特定内容、编辑内容或访问管理面板）。
 * 当您想要授权用户时，必须使用 IAuthorizationService 来检查当前用户是否具有您作为参数提供的权限
 * （即用户是否在具有给定权限的角色中）。
 * 有些权限可以单独使用，有些可以与对象一起检查（例如与 ContentItem 一起的 ViewContent 权限）。
 *
 * 在这里您将看到授权当前用户编辑 Person 内容项的示例。我们还将学习如何创建名为 "Manage Persons" 的自定义权限以及如何使用它。
 */

using Ruitu.Edu.TrainingDemo.Permissions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Localization;
using OrchardCore.ContentManagement;
using OrchardCore.DisplayManagement.Notify;
using System.Threading.Tasks;

namespace Ruitu.Edu.TrainingDemo.Controllers;

/// <summary>
/// 授权控制器 - 演示权限验证和授权机制
/// 
/// 主要用途：
/// - 演示如何检查用户权限
/// - 展示内容项的所有权权限验证
/// - 演示自定义权限的使用
/// 
/// 使用场景：
/// - 验证用户是否可以编辑特定内容
/// - 检查用户是否具有管理权限
/// - 实现细粒度的访问控制
/// 
/// 设计原因：
/// - 遵循 Orchard Core 的授权最佳实践
/// - 演示权限验证的不同方式
/// - 展示内容所有权的重要性
/// 
/// 设计模式：
/// - 授权模式：使用 IAuthorizationService 进行权限验证
/// - 依赖注入模式：通过构造函数注入所需服务
/// - 通知模式：使用 INotifier 显示用户反馈
/// </summary>
public sealed class AuthorizationController : Controller
{
    private readonly IAuthorizationService _authorizationService;
    private readonly IContentManager _contentManager;
    private readonly INotifier _notifier;
    private readonly IHtmlLocalizer H;

    /// <summary>
    /// 构造函数 - 通过依赖注入初始化所需服务
    /// </summary>
    /// <param name="authorizationService">授权服务，用于权限验证</param>
    /// <param name="contentManager">内容管理器，用于内容操作</param>
    /// <param name="notifier">通知器，用于显示用户消息</param>
    /// <param name="htmlLocalizer">HTML 本地化器，用于多语言支持</param>
    public AuthorizationController(
        IAuthorizationService authorizationService,
        IContentManager contentManager,
        INotifier notifier,
        IHtmlLocalizer<AuthorizationController> htmlLocalizer)
    {
        _authorizationService = authorizationService;
        _contentManager = contentManager;
        _notifier = notifier;
        H = htmlLocalizer;
    }

    /// <summary>
    /// 检查是否可以编辑人员内容项 - 演示内容项权限验证
    /// 
    /// 在这里我们将创建一个 Person 内容项并检查用户是否有权限编辑它。
    /// 检查您是否可以查看或编辑特定项目是很常见的 - 如果您使用内置 URL 如 /Contents/Item/Display/{id} 查看内容项，也会发生这种情况。
    /// </summary>
    /// <returns>如果授权成功则重定向到主页，否则返回未授权状态</returns>
    public async Task<IActionResult> CanEditPerson()
    {
        // 创建用于测试的内容项（不会被持久化）。
        var person = await _contentManager.NewAsync(ContentTypes.PersonPage);

        // 检查用户是否有权限编辑内容项。当您检查内容相关权限（ViewContent、EditContent、PublishContent 等）时，
        // 检查您自己的内容项（即所有者是您）和他人内容项的权限之间存在差异。
        // 当您是内容项的所有者时，将检查 ViewOwnContent、EditOwnContent、PublishOwnContent 等权限。
        // 这是自动的，所以您不需要直接使用它们。对于这个新创建的 Person 项目，所有者为空，所以将使用 EditContent 权限。
        if (!await _authorizationService.AuthorizeAsync(User, OrchardCore.Contents.CommonPermissions.EditContent, person))
        {
            // 使用此辅助方法返回 401 状态码。虽然返回 404 (NotFound()) 是防止枚举攻击的好做法。
            return Unauthorized();
        }

        // 为了保持演示简短，只显示关于成功授权的通知并返回到主页。
        await _notifier.InformationAsync(H["You are authorized to edit Person content items."]);

        return Redirect("~/");
    }

    // 下一站：Permissions/PersonPermissions

    /// <summary>
    /// 检查是否可以管理人员 - 演示自定义权限验证
    /// 
    /// 我们之前定义了一个 ManagePersons 权限，默认情况下会添加到管理员用户。
    /// 如果当前用户没有管理员角色，您可以在仪表板上添加它。
    /// 由于此权限可以在没有任何对象作为上下文的情况下检查，所以省略了第三个参数。
    /// </summary>
    /// <returns>如果授权成功则重定向到主页，否则返回未授权状态</returns>
    public async Task<IActionResult> CanManagePersons()
    {
        // 我们之前定义了一个 ManagePersons 权限，默认情况下会添加到管理员用户。
        // 如果当前用户没有管理员角色，您可以在仪表板上添加它。
        // 由于此权限可以在没有任何对象作为上下文的情况下检查，所以省略了第三个参数。
        if (!await _authorizationService.AuthorizeAsync(User, PersonPermissions.ManagePersons))
        {
            return Unauthorized();
        }

        await _notifier.InformationAsync(H["You are authorized to manage persons."]);

        return Redirect("~/");
    }
}

// 训练部分结束：权限和授权

// 下一站：Controllers/AdminController
